﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MtApi;
using MTEA.ComStruct;
using MTEA.State;
using MTEA.WatchDog;
using MTEA.LIB.Analysis.KDJ;
using MTEA.LIB.Analysis;

namespace MTEA
{
    

    enum MACDResult
    {
        None,
        Buy,
        Sell
    }

    //修改建议：
    //2.波动单开单需要KDJ 和 Bolling配合
    //3.趋势单平单，看MA5的与上线bolling位置
    class Experter : IStateMachine
    {
        public static readonly string Tag = "Experter";
        private MTState currentState;

        private MtApiClient client;
        private string symbol;
        private int timeframes = (int)ENUM_TIMEFRAMES.PERIOD_M1;

        private MTStateContext context;
        public string Symbol
        {
            get { return symbol; }
        }
        public Experter(MtApiClient client, string symbol)
        {
            context = new MTStateContext(symbol, client);
            this.symbol = symbol;
            this.client = client;
            setState(new StartState(this, client));
        }

        public void setState(MTState state)
        {
            Debug.Assert(state != null);
            if (currentState != null)
            {
                MTWatchDog.Instance.RemoveWatchable(currentState);
            }
            MTWatchDog.Instance.AddWatchable(state);
            currentState = state;

        }
        public void Tick()
        {
            lock (this)
            {
                try
                {
                    currentState.Execute(context);
                }
                catch (Exception ex)
                {
                    MTLogcat.f(Tag,ex);
                    throw;
                }
               
            }
        }

        #region TechnicalAnalysis
        private double getMACDMain(int shift)
        {
            return client.iMACD(symbol, timeframes, 12, 26, 9, 0, 0, shift);
        }
        private double getMACDSignal(int shift)
        {
            return client.iMACD(symbol, timeframes, 12, 26, 9, 0, 1, shift);
        }
       

        private double getMA5(int shift)
        {
            return client.iMA(symbol, timeframes, 5, 0, 0, 0, shift);
        }

        private double getMA20(int shift)
        {
            return client.iMA(symbol, timeframes, 20, 0, 0, 0, shift);
        }

       

        private KDJResult getKDJResult()
        {
            double prevMain = client.iStochastic(symbol, timeframes, 9, 3, 3, 0, 0, 0, 4);
            double prevSignal = client.iStochastic(symbol, timeframes, 9, 3, 3, 0, 0, 1, 4);
            double main = client.iStochastic(symbol, timeframes, 9, 3, 3, 0, 0, 0, 2);
            double signal = client.iStochastic(symbol, timeframes, 9, 3, 3, 0, 0, 1, 2);
            //if (!isValid(prevMain) || !isValid(prevSignal) || !isValid(main) || !isValid(signal))
            //{
                //MTLogcat.d(Tag, "Checking isUnValid ");
                //MTLogcat.d(Tag, string.Format("                 main: {0} signal : {1}", main, signal));
                //MTLogcat.d(Tag, string.Format("                 prevMain: {0} prevSignal : {1}", prevMain, prevSignal));
               // return KDJResult.None;
            //}
            MTLogcat.d(Tag, "Checking KDJ isValid ");
            MTLogcat.d(Tag, string.Format("                 main: {0} signal : {1}", main, signal));
            MTLogcat.d(Tag, string.Format("                 prevMain: {0} prevSignal : {1}", prevMain, prevSignal));
            //if (main > signal && prevMain < prevSignal && main < 25 && signal < 25 && prevMain < 25 && prevSignal < 25)
            //{
            //    //buy
            //    return KDJResult.Buy;
            //}

            if (main < 15 && signal < 15 )
            {
                //buy
                return KDJResult.Buy;
            }

            // else if (main < signal && prevMain > prevSignal && main >75 && signal > 75 && prevMain >75 && prevSignal >75 )
            else if (main > 90 && signal > 90)
            {
                //sell
                return KDJResult.Sell;
            }
            return KDJResult.None;
        }

        private MACDResult getMACDResult()
        {
            double signal = getMACDSignal(1);
            if (signal < 0)
            {
                double tmpMain = 0;
                for (int i = 1; i < 3; i++)
                {
                    if (tmpMain < getMACDMain(i))
                    {
                        return MACDResult.None;
                    }
                    tmpMain = getMACDMain(i);
                }
                return MACDResult.Buy;
            }
            else if (signal > 0)
            {
                double tmpMain = 0;
                for (int i = 1; i < 3; i++)
                {
                    if (tmpMain > getMACDMain(i))
                    {
                        return MACDResult.None;
                    }
                    tmpMain = getMACDMain(i);
                }
                return MACDResult.Sell;
            }
            return MACDResult.None;
        }


        private bool isValid(double value)
        {
            return value < 20 || value > 80;
        }
        #endregion

        class StartState : ExperterState
        {
            private CircularBuffer<KDJCrossResult> kdjCrossResults;
            public static readonly string Tag = Experter.Tag + "_StartState";
            public StartState(Experter experter, MtApiClient client) : base(experter, client)
            {
                kdjCrossResults=new CircularBuffer<KDJCrossResult>(20);
                var defaultKDJCross = new KDJCrossResult()
                {
                    CrossTye = KDJCrossType.None,
                    Value = double.MinValue,
                    Time = DateTime.MinValue
                };
                for (int i = 0; i < 20; i++)
                {
                    kdjCrossResults.Add(defaultKDJCross);
                }
            }

            public override string Name
            {
                get
                {
                    return Tag;
                }
            }

            private void analyzeKDJ()
            {
                var result = client.getKDJCross(symbol, timeframes, 1, 2);
                if (result.CrossTye!=KDJCrossType.None)
                {
                    kdjCrossResults.Add(result);
                }
            }

            private KDJCrossResult getLastKDJCross()
            {
                //Array.Sort(kdjCrossResults.CopyArray());
                return null;
            }
            public override void Execute(MTStateContext context)
            {
                base.Execute(context);
                MTLogcat.d(Tag, "Checking ");
                analyzeKDJ();
                //kdj波动方式下单
                //bolling open
                if (client.isBollingOpen(symbol,timeframes))
                {
                    var isUpTrend = client.getMA5(symbol, timeframes, 1) >= client.getBollingMain(symbol, timeframes, 1);
                    if (isUpTrend)
                    {
                        
                    }
                    else
                    {
                        
                    }

                    //看涨
                    if (context.BuyOrder(1))
                    {
                        var orderInfo = context.OrderInfo;
                        Debug.Assert(orderInfo != null);
                        orderInfo.OrderInfoType = MTOrderInfoType.Wave;
                        setNextState(new HoldingState(experter, client));
                        MTLogcat.i(Tag, "OrderSendBuy successfully new tickId : " + context.TickId);
                    }
                    else
                    {
                        MTLogcat.w(Tag, "OrderSendBuy Failed Error : " + client.ErrorDescription(client.GetLastError()));
                    }
                }
                else if (experter.getKDJResult() == KDJResult.Sell)
                {
                    //看跌
                    if (context.SellOrder(1))
                    {
                        var orderInfo = context.OrderInfo;
                        Debug.Assert(orderInfo != null);
                        orderInfo.OrderInfoType = MTOrderInfoType.Wave;
                        setNextState(new HoldingState(experter, client));
                        MTLogcat.i(Tag, "OrderSendSell successfully new tickId : " + context.TickId);
                    }
                    else
                    {
                        MTLogcat.w(Tag, "OrderSendSell Failed Error : " + client.ErrorDescription(client.GetLastError()));
                    }
                }
            }


        }
        class HoldingState : ExperterState
        {
            public static readonly string Tag = Experter.Tag + "_HoldingState";
            public override string Name
            {
                get
                {
                    return Tag;
                }
            }
            public HoldingState(Experter experter, MtApiClient client) : base(experter, client)
            {

            }

            public override void Execute(MTStateContext context)
            {
                base.Execute(context);
                MTLogcat.d(Tag, "Executing");
                context.RefreshOrderInfo();
                Debug.Assert(context.TickId != -1);
                var orderInfo = context.OrderInfo;
                double tmpMain;
                var order = orderInfo.Order;
                DateTime currentTime = client.iTime(context.Symbol, (ChartPeriod)experter.timeframes, 0);
                DateTime openTime = order.OpenTime;
                switch (orderInfo.OrderInfoType)
                {
                    //处理波动单(wave)
                    case MTOrderInfoType.Wave:
                        if (order.Operation == TradeOperation.OP_BUY)
                        {
                            //买单

                            //波动单下单2分钟不盈利,MACD发出卖信号
                            //MACD 发出卖信号，KDJ发出超买信号
                            bool tooLongTime = (currentTime-openTime ).Minutes > 3;
                            //bool lossPric = (client.iClose(context.Symbol, (ChartPeriod)experter.timeframes, 0) <
                            //                 order.OpenPrice);
                            bool lossPric = order.Profit<-5;
                            //bool macdSellSig = experter.getMACDResult() == MACDResult.Sell;
                            bool maSellSig = experter.getMA5(1)<experter.getMA20(1);

                            MTLogcat.i(Tag, string.Format("TooLongTime: {0} LossPric : {1} MASellSig : {2}", tooLongTime, lossPric, maSellSig));
                            MTLogcat.d(Tag, string.Format("CurrentTime: {0} OpenTime : {1} Profit : {2}", currentTime, openTime, order.Profit));
                            MTLogcat.d(Tag, string.Format("MA5: {0} MA20 : {1}", experter.getMA5(1), experter.getMA20(1)));
                            if (tooLongTime && lossPric && maSellSig)
                            {
                                setNextState(new SwitchState(experter, client));
                                return;
                            }


                            var kdjResult = experter.getKDJResult();
                            if (kdjResult==KDJResult.Sell)
                            {
                                closeOrder(context);
                            }
                        }
                        else if (orderInfo.Order.Operation == TradeOperation.OP_SELL)
                        {
                            //卖单
                            //波动单下单2分钟不盈利,MACD发出卖信号
                            //MACD 发出买信号，KDJ发出超卖信号
                            bool tooLongTime = (currentTime-openTime).Minutes > 3;
                            //bool lossPric = (client.iClose(context.Symbol, (ChartPeriod)experter.timeframes, 0) >
                            //                 order.OpenPrice);
                            bool lossPric = order.Profit < -5;
                            //bool macdSellSig = experter.getMACDResult() == MACDResult.Buy;
                            bool maSellSig = experter.getMA5(1) > experter.getMA20(1);
                            MTLogcat.i(Tag, string.Format("TooLongTime: {0} LossPric : {1} MASellSig : {2}", tooLongTime, lossPric, maSellSig));
                            MTLogcat.d(Tag, string.Format("CurrentTime: {0} OpenTime : {1} Profit : {2}", currentTime, openTime, order.Profit));
                            MTLogcat.d(Tag, string.Format("MA5: {0} MA20 : {1}", experter.getMA5(1), experter.getMA20(1)));
                            if (tooLongTime && lossPric && maSellSig)
                            {
                                setNextState(new SwitchState(experter, client));
                                return;
                            }

                            var kdjResult = experter.getKDJResult();
                            if (kdjResult == KDJResult.Buy)
                            {
                                closeOrder(context);
                            }

                        }
                        else
                        {
                            MTLogcat.f(Tag, "Invalid Order Type");
                            throw new Exception("Invalid Order Type");
                        }
                        break;
                    //处理趋势单(trend)
                    case MTOrderInfoType.Trend:
                        var trendOrder = orderInfo.Order;
                        if (trendOrder.Operation == TradeOperation.OP_SELL)
                        {
                            bool maSignal = experter.getMA5(1) > experter.getMA20(1);
                            if(maSignal)
                            {
                                closeOrder(context);
                            }
                            //tmpMain = double.MinValue;
                            //for (int i = 2; i < 4; i++)
                            //{
                            //    if (tmpMain > experter.getMACDMain(i))
                            //    {
                            //        return;
                            //    }
                            //    tmpMain = experter.getMACDMain(i);
                            //}
                            //if (experter.getMACDMain(1) > experter.getMACDMain(2))
                            //{
                            //    closeOrder(context);
                            //}
                        }
                        else if (orderInfo.Order.Operation == TradeOperation.OP_BUY)
                        {
                            bool maSignal = experter.getMA5(1) < experter.getMA20(1);
                            if (maSignal)
                            {
                                closeOrder(context);
                            }
                            //tmpMain = double.MaxValue;
                            //for (int i = 2; i < 4; i++)
                            //{
                            //    if (tmpMain < experter.getMACDMain(i))
                            //    {
                            //        return;
                            //    }
                            //    tmpMain = experter.getMACDMain(i);
                            //}
                            //if (experter.getMACDMain(1) < experter.getMACDMain(2))
                            //{
                            //    closeOrder(context);
                            //}
                        }
                        else
                        {
                            MTLogcat.f(Tag, "Invalid Order Type");
                            throw new Exception("Invalid Order Type");
                        }
                        break;
                }
            }

            private void closeOrder(MTStateContext context)
            {
                var result = context.CloseOrder();
                if (result)
                {
                    MTLogcat.i(Tag, "OrderClose successfully tickId : " + context.TickId);
                    setNextState(new StopState(experter, client));
                }
                else
                {
                    MTLogcat.w(Tag, "OrderClose Failed Error : " + client.ErrorDescription(client.GetLastError()));
                }
            }
        }

        class SwitchState : ExperterState
        {
            public static readonly string Tag = Experter.Tag + "_SwitchState";
            public SwitchState(Experter experter, MtApiClient client) : base(experter, client)
            {
            }

            public override void Execute(MTStateContext context)
            {
                base.Execute(context);
                MTLogcat.d(Tag, "Executing");
                context.RefreshOrderInfo();
                var orderInfo = context.OrderInfo;
                Debug.Assert(orderInfo != null);
                Debug.Assert(context.TickId != -1);

                switch (orderInfo.OrderInfoType)
                {
                    case MTOrderInfoType.Wave:
                        var opt = orderInfo.Order.Operation;
                        bool result = context.CloseOrder();
                        if (result)
                        {
                            MTLogcat.i(Tag, "OrderClose successfully tickId : " + context.TickId);
                            if (opt == TradeOperation.OP_BUY)
                            {
                                result = context.SellOrder(2);
                                if (result)
                                {
                                    orderInfo = context.OrderInfo;
                                    Debug.Assert(orderInfo != null);
                                    Debug.Assert(context.TickId != -1);
                                    orderInfo.OrderInfoType = MTOrderInfoType.Trend;
                                    setNextState(new HoldingState(experter, client));
                                    MTLogcat.i(Tag, "SellOrder successfully new tickId : " + context.TickId);
                                }
                                else
                                {
                                    MTLogcat.w(Tag, "SellOrder Failed Error : " + client.ErrorDescription(client.GetLastError()));
                                }
                            }
                            else if (opt == TradeOperation.OP_SELL)
                            {
                                result = context.BuyOrder(2);
                                if (result)
                                {
                                    orderInfo = context.OrderInfo;
                                    Debug.Assert(orderInfo != null);
                                    Debug.Assert(context.TickId != -1);
                                    orderInfo.OrderInfoType = MTOrderInfoType.Trend;
                                    setNextState(new HoldingState(experter, client));
                                    MTLogcat.i(Tag, "BuyOrder successfully new tickId : " + context.TickId);
                                }
                                else
                                {
                                    MTLogcat.w(Tag, "BuyOrder Failed Error : " + client.ErrorDescription(client.GetLastError()));
                                }
                            }
                            else
                            {
                                MTLogcat.f(Tag, "Invalid Order Type");
                                throw new Exception("Invalid Order Type");
                            }
                        }
                        else
                        {
                            MTLogcat.w(Tag, "OrderClose Failed Error : " + client.ErrorDescription(client.GetLastError()));
                        }
                        break;
                    default:
                        MTLogcat.f(Tag, "Invalid OrderInfoType");
                        throw new Exception("Invalid OrderInfoType");
                }
            }


        }

        #region tmp
        class StopState : ExperterState
        {
            public static readonly string Tag = Experter.Tag + "_StopState";
            public StopState(Experter experter, MtApiClient client) : base(experter, client)
            {
            }

            public override void Execute(MTStateContext context)
            {
                base.Execute(context);
                MTLogcat.i(Tag, "Executing");
                experter.setState(new StartState(experter, client));
            }
        }


        class ExperterState : MTState
        {
            protected Experter experter;
            protected string symbol;
            protected int timeframes;

            public ExperterState(Experter experter, MtApiClient client) : base(experter, client)
            {
                this.experter = experter;
                symbol = experter.symbol;
                timeframes = experter.timeframes;
            }
        }
        #endregion
    }
}
